package MyGui;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.text.*;

import DistSys.*;
import Element.*;
import Element.Geom.*;

public class QueryDlg extends MyDlgFrame implements ActionListener, WindowListener{
    private ZoneComboBox comboChoice;
    private JButton      btnSearch;

    private JTextField[] tfsWeight;
    private JComboBox[]  combosField;
    private JButton[]    btnsRD;

    private ElemListScrollPane scrollPane;
    private JButton   btnGoto, btnAddSelection;
    private JCheckBox checkPercent,
                      checkPUL; // per unit of length

    private JList lstType;

    public QueryDlg(MainFrame parent, String title) {
        super(parent, title);
        try {
            Container pane = this.getContentPane();
            pane.setLayout(gridBagLayout);

            add(pane, createNorthWestPanel(), 0, 0, 1, 1, 1, 0,
                GridBagConstraints.WEST, GridBagConstraints.BOTH,
                GAP, GAP, 0, 0);

            add(pane, createNorthEastPanel(), 1, 0, 1, 2, 1, 0,
                GridBagConstraints.WEST, GridBagConstraints.BOTH,
                GAP, GAP, 0, GAP);

            add(pane, createSouthWestPanel(), 0, 1, 1, 2, 1, 1,
                GridBagConstraints.WEST, GridBagConstraints.BOTH,
                GAP, GAP, GAP, 0);

            add(pane, createSouthEastPanel(), 1, 2, 1, 1, 1, 1,
                GridBagConstraints.WEST, GridBagConstraints.BOTH,
                GAP, GAP, GAP, GAP);

            this.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
            this.setSize(600,420);
            this.setLocation(200,200);
            // this.setResizable(false);
            this.addWindowListener(this);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }

    private JPanel createNorthWestPanel(){
        JPanel p = new JPanel(gridBagLayout);
        p.setBorder(new TitledBorder(" Search Area "));

        comboChoice = new ZoneComboBox();

        btnSearch = new JButton("Search");
        btnSearch.setMnemonic('S');
        btnSearch.addActionListener(this);

        add(p, comboChoice, 0, 0, 1, 1, 1, 0, GridBagConstraints.CENTER,
            GridBagConstraints.HORIZONTAL, 0, GAP, GAP, 0);
        add(p, btnSearch, 1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER,
            GridBagConstraints.HORIZONTAL, 0, GAP, GAP, GAP);

        return p;
    }

    private JPanel createNorthEastPanel(){
        JPanel p = new JPanel(gridBagLayout);
        p.setBorder(new TitledBorder(
            " Search Criterion (weighted sum of fields) "));

        JLabel lblOne = new JLabel("Weight");
        // a label which is long enough to prevent floating
        JLabel lblTwo = new JLabel(
            "Data/Result Field                                      ");
        JLabel lblThree = new JLabel("D/R");
        lblOne.setFont( GuiConst.fontDefault );
        lblTwo.setFont( GuiConst.fontDefault );
        lblThree.setFont( GuiConst.fontDefault );

        tfsWeight = new JTextField[5];
        combosField = new JComboBox[5];
        btnsRD = new JButton[5];
        InputVerifier verifier = new DoubleVerifier();
        for(int i=0;i<5;i++){
            tfsWeight[i] = new JTextField("0",6);
            tfsWeight[i].setInputVerifier(verifier);
            if( i<3) {
                combosField[i] = new JComboBox(SysConst.ssResultField);
                btnsRD[i] = new JButton("R");
            } else {
                combosField[i] = new JComboBox(SysConst.ssDataField);
                btnsRD[i] = new JButton("D");
            }
            tfsWeight[i].setFont(GuiConst.fontDefault);
            combosField[i].setFont(GuiConst.fontDefault);
            btnsRD[i].setFont(GuiConst.fontDefault);
            btnsRD[i].addActionListener(this);
        }
        tfsWeight[0].setText("1");
        combosField[0].setSelectedIndex(
            SysConst.mapResultIndexFromValueToDisplay(AbsElem.RES_MAIFI));
        combosField[1].setSelectedIndex(
            SysConst.mapResultIndexFromValueToDisplay(AbsElem.RES_SAIFI));
        combosField[2].setSelectedIndex(
            SysConst.mapResultIndexFromValueToDisplay(AbsElem.RES_SAIDI));
        combosField[3].setSelectedIndex(
            SysConst.mapResultIndexFromValueToDisplay(AbsElem.DATA_TEMP_FAULT));
        combosField[4].setSelectedIndex(
            SysConst.mapResultIndexFromValueToDisplay(AbsElem.DATA_SUST_FAULT));

        add(p, lblOne, 0, 0, 1, 1, 1, 0, GridBagConstraints.WEST,
            GridBagConstraints.NONE, 0, GAP, 0, 0);
        add(p, lblTwo, 1, 0, 1, 1, 1, 0, GridBagConstraints.WEST,
            GridBagConstraints.NONE, 0, GAP, 0, 0);
        add(p, lblThree, 2, 0, 1, 1, 0, 0, GridBagConstraints.CENTER,
            GridBagConstraints.NONE, 0, GAP, 0, GAP);
        for(int i=0; i<5;i++){
            int iBottom = (i == 4 ? GAP : 0);
            add(p, tfsWeight[i], 0, i+1, 1, 1, 1, 0, GridBagConstraints.WEST,
                GridBagConstraints.HORIZONTAL, 0, GAP, iBottom, 0);
            add(p, combosField[i], 1, i+1, 1, 1, 1, 0, GridBagConstraints.WEST,
                GridBagConstraints.HORIZONTAL, 0, GAP, iBottom, 0);
            add(p, btnsRD[i], 2, i+1, 1, 1, 0, 0, GridBagConstraints.WEST,
                GridBagConstraints.HORIZONTAL, 0, GAP, iBottom, GAP);
        }

        return p;
    }

    private JPanel createSouthWestPanel(){
        JPanel p = new JPanel(gridBagLayout);
        p.setBorder(new TitledBorder(" Results "));

        scrollPane = new ElemListScrollPane( mainFrame, ElemListTableModel.PANE_QUERY );

        checkPUL = new JCheckBox("Use per unit values for line and cables");
        checkPUL.setMnemonic('U');
        checkPUL.setSelected(false);
        checkPUL.addActionListener(this);

        checkPercent = new JCheckBox("Convert score to a percentage");
        checkPercent.setMnemonic('C');
        checkPercent.setSelected(false);
        checkPercent.addActionListener(this);

        btnGoto = new JButton("Goto");
        btnGoto.setMnemonic('G');
        btnGoto.addActionListener(this);
        btnAddSelection = new JButton("Add to Selection");
        btnAddSelection.setMnemonic('A');
        btnAddSelection.addActionListener(this);

        add(p, scrollPane, 0, 0, 2, 1, 1, 1,
            GridBagConstraints.WEST, GridBagConstraints.BOTH, 0, GAP, 0, GAP);
        add(p, checkPercent, 0, 1, 2, 1, 1, 0, GridBagConstraints.WEST,
            GridBagConstraints.NONE, 0, GAP, 0, GAP);
        add(p, checkPUL, 0, 2, 2, 1, 1, 0, GridBagConstraints.WEST,
            GridBagConstraints.NONE, 0, GAP, 0, GAP);
        add(p, btnGoto, 0, 3, 1, 1, 1, 0, GridBagConstraints.CENTER,
            GridBagConstraints.HORIZONTAL, 0, GAP, GAP, 0);
        add(p, btnAddSelection, 1, 3, 1, 1, 1, 0, GridBagConstraints.CENTER,
            GridBagConstraints.HORIZONTAL, 0, GAP, GAP, GAP);
        return p;
    }

    private JPanel createSouthEastPanel(){
        JPanel p = new JPanel(gridBagLayout);
        p.setBorder(new TitledBorder(" Component type to include "));

        lstType = new JList(SysConst.ssType);
        lstType.setSelectedIndex(0);
        JScrollPane scrollPaneForRoots = new JScrollPane(lstType);
        scrollPaneForRoots.setPreferredSize(new Dimension(120,140));

        add(p, scrollPaneForRoots, 0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER,
            GridBagConstraints.BOTH, 0, GAP, GAP, GAP);
        return p;
    }

    public void actionPerformed(ActionEvent evt){
        Object src = evt.getSource();

        if (src == btnSearch) {
            double dZone;
            try {
                dZone = comboChoice.getSelectedZone();
            } catch(ZoneComboBox.ZoneException e) {
                JOptionPane.showMessageDialog(this,
                  "'" + e.getMessage() + "' is not a valid zone.");
                return;
            }

            if (lstType.getSelectionModel().getMinSelectionIndex() < 0 ||
                lstType.isSelectedIndex(1)) {
                JOptionPane.showMessageDialog(this, "No components selected.");
                return;
            }

            //find parameters
            int[] index = new int[5];
            boolean[] isData = new boolean[5];
            double[] w={1,0,0,0,0};
            for(int i=0;i<5;i++){
                w[i] = Double.parseDouble( tfsWeight[i].getText() );
                index[i]=combosField[i].getSelectedIndex();
                if( btnsRD[i].getText().equals("D") ) isData[i] = true;
                else isData[i] = false;
            }

            //get compoment types
            AbsElem elem;
            AbsElemGeom geom;
            double highest = Double.NEGATIVE_INFINITY;

            int indexOfType;
            int sz = Sys.elemList.size();
            TreeSet resultsInTree = new TreeSet( new QueryScoreComparator() );
            for(int i=0; i<sz; i++){
                elem = (AbsElem) Sys.elemList.elementAt(i);
                elem.setResult(AbsElem.RES_QUERY_SCORE, 0.0);//elem.queryScore = 0.0;
                elem.setResult(AbsElem.RES_QUERY_SCORE_PERC, 0.0);//elem.queryScorePercent = 0.0;

                //in a different zone
                if(!Zone.getZoneFits(dZone, elem)) { continue; }

                //different type
                indexOfType = SysConst.mapTypeToIndex(elem.getType());
                if( lstType.isSelectedIndex(0)==false && //[All] option
                    lstType.isSelectedIndex(indexOfType)==false) { continue; }

                //zero-length lines/cables
                if( elem instanceof Line &&
                    elem.getData( AbsElem.DATA_LENGTH)==0.0){ continue; }

                calcQueryScore( elem, w, index, isData, checkPUL.isSelected() );

                if ( elem.getResult(AbsElem.RES_QUERY_SCORE) > highest) {
                    highest = elem.getResult(AbsElem.RES_QUERY_SCORE);
                }
                resultsInTree.add(elem);
            }//end of for loop

            scrollPane.getData().removeAllElements();
            scrollPane.getData().addAll( resultsInTree);

            if ( Math.abs( highest ) > 0) {
                sz= scrollPane.getData().size();
                double dScore;
                for(int i=0; i<sz; i++){
                    elem = (AbsElem) scrollPane.getData().elementAt(i);
                    dScore = elem.getResult(AbsElem.RES_QUERY_SCORE);
                    dScore = dScore * 100.0 / highest ;
                    elem.setResult( AbsElem.RES_QUERY_SCORE_PERC, dScore );
                }
            }

            scrollPane.getModel().fireTableDataChanged();
            if( scrollPane.getData().size() == 0 ){
                JOptionPane.showMessageDialog(this, "No match found.");
            }

            // Reset current shading
            mainFrame.menuBar.setCurrentShadingOption(Sys.prjInfo.shadingCat);
            mainFrame.mainPanel.setNeedRedraw(true);
            mainFrame.mainPanel.repaint();
        } else if (src == btnGoto) {
            scrollPane.gotoSelectedElem();
        } else if (src == btnAddSelection) {
            scrollPane.addToSelection();
        } else if (src == checkPUL) {
            scrollPane.getModel().updateQueryScoreInPerUnit( checkPUL.isSelected());
            scrollPane.getModel().fireTableDataChanged();
        } else if (src == checkPercent) {
            //no need to sort. Just display based on
            scrollPane.getModel().setIsQueryScoreInPercent( checkPercent.isSelected() );
            scrollPane.getModel().fireTableDataChanged();
        } else {
            for (int i = 0; i < 5; i++) {
                if (src == btnsRD[i]) {
                    if (btnsRD[i].getText().equals("R")) {
                        btnsRD[i].setText("D");
                        combosField[i].setModel(
                            new DefaultComboBoxModel(SysConst.ssDataField));
                    } else {
                        btnsRD[i].setText("R");
                        combosField[i].setModel(
                            new DefaultComboBoxModel(SysConst.ssResultField));
                    }
                    return;
                }
            }
        }
    }//end of actionPerformed()

    private double calcQueryScore(AbsElem elem, double[] w, int[] index,
                                 boolean[] isData, boolean bPUL ) {
        int    iValueIndex;
        double dValue=0;
        double dScore=0;
        for(int i=0;i<5;i++){
            if( isData[i] ) {
                iValueIndex = SysConst.mapDataIndexFromDisplayToValue( index[i] ) ;
                if( iValueIndex>=0 && iValueIndex<AbsElem.NUM_DATA_FIELD ){
                    dValue = elem.getData( iValueIndex);
                }
                //for lines/cables, some data filds are in per unit.
                if( elem instanceof Line && elem.isDataRelatedToLength(iValueIndex)){
                    dValue *= elem.getData(AbsElem.DATA_LENGTH);
                }
            } else {
                iValueIndex = SysConst.mapResultIndexFromDisplayToValue( index[i]);
                if( iValueIndex>=0 && iValueIndex < AbsElem.NUM_RES_FIELD ){
                    dValue = elem.getResult( iValueIndex);
                }
            }
            dScore += ( dValue * w[i] );
        }

        //After for loop, dScore is length-neutral;
        //Now make it related to length...
        if( bPUL && elem instanceof Line){
            dScore /= elem.getData(AbsElem.DATA_LENGTH);
        }

        elem.setResult( AbsElem.RES_QUERY_SCORE, dScore);
        return dScore;
    }//end of calcQueryScore();

    /*======================================
      Implement WindowListener interfaces
    ======================================*/
    public void windowOpened( WindowEvent evt ){ }
    public void windowClosed( WindowEvent evt ){ }
    public void windowIconified( WindowEvent evt ){ }
    public void windowDeiconified( WindowEvent evt ){ }
    public void windowActivated( WindowEvent evt ){ scrollPane.updateTable(); }
    public void windowDeactivated( WindowEvent evt ){ }
    public void windowClosing( WindowEvent evt ){ }
}

//NOTE: based on AbsElem.queryScore ;  highest score appears first.
class QueryScoreComparator implements Comparator{
    public int compare(Object a, Object b){
        AbsElem elemA = (AbsElem) a;
        AbsElem elemB = (AbsElem) b;
        // Highest score appears first!
        if( elemA.getResult(AbsElem.RES_QUERY_SCORE) <
            elemB.getResult(AbsElem.RES_QUERY_SCORE) )
                return 1;
        else    return -1;
    }
}